﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace M3G {
    public class TextureCombiner : Object3D {
        #region Constant
        // Function
        public const int Add         = 16;
        public const int AddSigned   = 17;
        public const int Dot3RGB     = 18;
        public const int Dot3RGBA    = 19;
        public const int Interpolate = 20;
        public const int Modulate    = 21;
        public const int Replace     = 22;
        public const int Subtract    = 23;

        // Source
        public const int Constant    = 28;
        public const int Primary     = 29;
        public const int Previous    = 30;
        public const int Texture     = 31;

        // Option flags
        public const int Invert      = 256;
        public const int Alpha       = 512;
        #endregion

        #region Field
        int   colorFunction;
        int[] colorSource;
        int   alphaFunction;
        int[] alphaSource;
        int   scaling;
        #endregion



        #region Method
        public TextureCombiner () {
            this.colorFunction = Modulate;
            this.colorSource = new int[3] { Texture, Previous, Constant | Alpha };
            this.alphaFunction = Modulate;
            this.alphaSource = new int[3] { Texture, Previous, Constant };
            this.scaling = 1 << 24 | 1 << 16 | 1 << 8 | 1 << 0;
        }

        protected TextureCombiner (TextureCombiner textureCombiner)
            : base (textureCombiner) {
            this.colorFunction = textureCombiner.colorFunction;
            this.colorSource   = (int[])textureCombiner.colorSource.Clone ();
            this.alphaFunction = textureCombiner.alphaFunction;
            this.alphaSource   = (int[])textureCombiner.alphaSource.Clone ();
            this.scaling       = textureCombiner.scaling;
        }

        public int ColorFunction {
            get { return colorFunction; }
        }

        public int AlphaFunction {
            get { return alphaFunction; }
        }

        public void SetFunctions (int colorFunction, int alphaFunction) {
            if (colorFunction < Add || colorFunction > Subtract
                || alphaFunction < Add || alphaFunction > Subtract) {
                throw new ArgumentException ("Blend functions must be one of symbolic constant defined in TextureCombiner, color=" + colorFunction + ",alpha=" + alphaFunction);
            }

            this.colorFunction = colorFunction;
            this.alphaFunction = alphaFunction;
        }


        public int[] ColorSource {
            get {
                return (int[])colorSource.Clone ();
            }
        }


        public void SetColorSource (int index, int source) {
            if (index < 0 || index > 2) {
                throw new ArgumentOutOfRangeException ("Index must be within [0,2], index=" + index);
            }
            if ((source & 0xff) < Constant || (source & 0xff) > Texture) {
                throw new ArgumentException ("Source must be one of symbolic constant define in TextureCominer and optionaly bitwised with [Alpha|Invert], src=" + source);
            }

            this.colorSource[index] = source;
        }

        public int[] AlphaSource {
            get {
                return (int[])alphaSource.Clone ();
            }
        }

        public void SetAlphaSource (int index, int source) {
            if (index < 0 || index > 2) {
                throw new ArgumentOutOfRangeException ("Index must be within [0,2], index=" + index);
            }
            if ((source & 0xff) < Constant || (source & 0xff) > Texture) {
                throw new ArgumentException ("Source must be one of symbolic constant define in TextureCominer and optionaly bitwised with Invert, src=" + source);
            }

            this.alphaSource[index] = source;
        }

        public int Scaling {
            get {
                return scaling;
            }
        }

        public void SetScaling (int colorScale, int alphaScale) {
            var r = (colorScale & 0x00ff0000) >> 16;
            var g = (colorScale & 0x0000ff00) >> 8;
            var b = (colorScale & 0x000000ff) >> 0;
            var a = (alphaScale & 0x000000ff) >> 0;
            if ((r != 1 && r != 2 && r != 4) || (g != 1 && g != 2 && g != 4) || (b != 1 && b != 2 && b != 4) || (a != 1 && a != 2 && a != 4)) {
                throw new ArgumentException ("Scale factor must be 1,2,4, r=" + r + ",g=" + g + ",b=" + b + ",a=" + a);
            }

            this.scaling = (a << 24 | r << 16 | g << 8 | b << 0);
        }

        public override Object3D Duplicate () {
            return new TextureCombiner (this);
        }
        #endregion
    }
}
